package core;

import gitobject.Blob;
import gitobject.GitObject;
import repository.Repository;
import stage.Index;
import java.io.File;
import java.util.ArrayList;

public class JitDiff {
    /**
     * command jit diff
     */
    public static void diff()throws Exception{
        File worktree = new File(Repository.getWorkTree());
        if (worktree.exists()){
            File[] fileList = worktree.listFiles();
            assert fileList != null;
            compare(fileList);
        }
    }

    /**
     * compare the files in worktree to stage
     */
    public static void compare(File[] fileList)throws Exception{
        for (File file:fileList){
            if(file.isFile()){
                String filePath = file.getPath().substring(Repository.getWorkTree().length()+1);
                String[] info = new Index().getInfo().get(filePath);
                if (info==null)
                    continue;
                String fileKey = info[1];
                String content = Blob.deserialize(fileKey).getValue();
                ArrayList<String> path = myers(content, GitObject.getValue(file));
                if (path.size() > 0){
                    System.out.println(file.getPath());
                    printDiff(path);
                }
            }else {
                if(file.getName().equals(".jit"))
                    continue;
                File[] innerList = file.listFiles();
                assert innerList != null;
                compare(innerList);
            }
        }
    }

    /**
     * algorithm myers diff
     */
    public static ArrayList<String> myers(String file1, String file2){
        String[] content1 = file1.split("\r\n");
        String[] content2 = file2.split("\r\n");
        int len1 = content1.length;
        int len2 = content2.length;
        int[][][] map = new int[len1 + 1][len2 + 1][2];

        //dynamic programing
        for (int i = 0; i < len1 + 1; i++)
            for (int j = 0; j < len2 + 1; j++) {
                if (i == 0) {
                    map[i][j][0] = j;
                    map[i][j][1] = 1;
                } else if (j == 0) {
                    map[i][j][0] = i;
                    map[i][j][1] = -1;
                } else {
                    if (content1[i - 1].equals(content2[j - 1])) {
                        int d3 = map[i - 1][j - 1][0];
                        int d2 = map[i][j - 1][0];
                        int d1 = map[i - 1][j][0];
                        if (d1 <= d3 && d1 <= d2) {
                            map[i][j][0] = map[i - 1][j][0] + 1;
                            map[i][j][1] = -1;
                        } else if (d3 < d1 && d3 <= d2) {
                            map[i][j][0] = map[i - 1][j - 1][0];
                            map[i][j][1] = 0;
                        } else {
                            map[i][j][0] = map[i][j - 1][0] + 1;
                            map[i][j][1] = 1;
                        }
                    } else {
                        int d2 = map[i][j - 1][0];
                        int d1 = map[i - 1][j][0];
                        if (d1 <= d2) {
                            map[i][j][0] = map[i - 1][j][0] + 1;
                            map[i][j][1] = -1;
                        } else {
                            map[i][j][0] = map[i][j - 1][0] + 1;
                            map[i][j][1] = 1;
                        }
                    }
                }
            }

        //reappearance modify path
        int i = len1, j = len2;
        ArrayList<String> path = new ArrayList<>();
        boolean changed = false;
        while (i > 0 || j > 0) {
            if (map[i][j][1] == -1) {
                i--;
                path.add(0, "-" + content1[i]);
                changed = true;
            } else if (map[i][j][1] == 0) {
                i--;
                j--;
                path.add(0, " " + content1[i]);
            } else {
                j--;
                path.add(0, "+" + content2[j]);
                changed = true;
            }
        }
        if (changed)
            return path;
        else
            return new ArrayList<>();
    }
    /**
     * print Shortest modification path
     */
    public static void printDiff(ArrayList<String> path){
        for (String s : path) System.out.println(s);
        System.out.println();
    }
}
